package com.zmx.core.server;

import com.zmx.core.dto.HeartInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.concurrent.BasicThreadFactory;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Description: 监测节点是否在线 判断服务是否在指定时间内发送心跳信息
 * @ClassName: OnlineServerMonitor
 * @Author zhaomxr
 * @Date 2021-12-23 09:22
 */
@Slf4j
public class MonitorClient {

    private int monitorIntervalTime;

    public MonitorClient() {
    }

    public MonitorClient(int monitorIntervalTime) {
        this.monitorIntervalTime = monitorIntervalTime;
    }

    public void start() {
        //设置为守护线程，不影响服务正常执行
        ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1,
                new BasicThreadFactory.Builder().namingPattern("monitorClient").daemon(true).build());

        executorService.scheduleAtFixedRate(new Runnable() {
            @Override
            public void run() {
                try {
                    log.info("-------------------监测节点是否在线 判断服务是否在指定时间内发送心跳信息-------------------");
                    Map<String, HeartInfo> infoInstance = ClientInstanceInfo.getInfoInstance();

                    if (infoInstance != null && infoInstance.size() > 0) {
                        //重新创建map集合，防止同一时间其他节点上下线影响当前判断，同时也避免集合遍历同时remove问题
                        Map<String, HeartInfo> infoMap = new HashMap<String, HeartInfo>(infoInstance.size());
                        infoMap.putAll(infoInstance);

                        for (String node : infoMap.keySet()) {
                            HeartInfo heartInfo = infoMap.get(node);
                            if (heartInfo != null) {
                                //上次发送节点心跳时间距离当前时间超过90秒没有更新，踢出此节点
                                long intervalTime = System.currentTimeMillis() - heartInfo.getLasttime().getTime();
                                if (intervalTime > 90000) {
                                    log.info("节点：{} 距离上次有效心跳间隔为：{} 超出最大限制[90]秒，自动下线！", node, intervalTime);
                                    ClientInstanceInfo.removeNode(node);
                                }
                            } else {
                                log.info("节点：{} 没有监测到心跳信息，自动下线！", node);
                                //如果没有心跳信息，认为此节点存在问题，踢出
                                ClientInstanceInfo.removeNode(node);
                            }
                        }
                    }
                } catch (Throwable t) {
                    log.error("MonitorClient error！");
                    t.printStackTrace();
                }
            }
        }, 3, monitorIntervalTime, TimeUnit.SECONDS);
    }
}
